package otherOrganizations;

import java.io.FileWriter;
import java.io.IOException;
import java.util.LinkedList;

import main.BuildModel;
import main.Organization;
import enviornment.Clock;
import enviornment.FloodWarning;
import agentsSME.BehaviourHistory;
import agentsSME.BehaviourSME;
import agentsSME.Employee;
import agentsSME.ManufactureSME;
import agentsSME.Message;
import agentsSME.MsgReceiver;
import agentsSME.MsgSender;
import agentsSME.SME;
import au.com.bytecode.opencsv.CSVWriter;
import repast.simphony.context.Context;
import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.engine.schedule.ScheduledMethod;
import repast.simphony.essentials.RepastEssentials;
import repast.simphony.util.ContextUtils;

public class ServiceCompany {

	private String name;
	private LinkedList<SME> premierList;
	private LinkedList<SME> serviceSequence;
	private LinkedList<ServiceRecord> serviceRequestList;
	private Employee[] teams;
	private int numberTeams;
	private boolean receivedNewRequest;
	private double time;
	private int msgNumber;
	private Clock clock;
	private int idleTeams;
	private MsgSender sender;
	private MsgReceiver receiver;
	private int limit; //for save data
	private CSVWriter writer;
	
	
	public ServiceCompany(int i, String name) {
		// TODO Auto-generated constructor stub		
		this.name=name;
		numberTeams=i;
		premierList=new LinkedList<SME>();
		serviceSequence=new LinkedList<SME>();
		serviceRequestList=new LinkedList<ServiceRecord>();
		teams=new Employee[i];
		receivedNewRequest=false;
		idleTeams=numberTeams;
		time=0.0;
		clock=new Clock();
		msgNumber=0;
		sender=new MsgSender(this);
		receiver = new MsgReceiver();
		limit=BuildModel.context.size()-BuildModel.smeContext.size()+1;
		initialiseTeams();
		String file = "_service_"+this.name;
		try {
			writer = new CSVWriter (new FileWriter(file+".csv"),',', CSVWriter.NO_QUOTE_CHARACTER);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private void initialiseTeams() {
		// TODO Auto-generated method stub
		for(int i=0; i<teams.length; i++){
			Employee e = new Employee(i);
			teams[i]=e;
		}
	}

	@ScheduledMethod(start =1, interval = 1, priority = 15)
	public void step(){
		time = RepastEssentials.GetTickCount();	
		clock.convertTicks(time);
		updateRequest();
		if(clock.getHour()>= BuildModel.START_WORK_HOUR && clock.getHour() <= BuildModel.END_WORK_HOUR){
			//allocate teams to premier customers first
			if(idleTeams>0 && !serviceRequestList.isEmpty()){
				for (ServiceRecord c: serviceRequestList){
					if(!c.isDone())
						if(isInPremierList(c)){
							for(BehaviourSME bsme: c.getServiceList()){
								if(idleTeams>0){
									System.out.print("premier   "+c.getSME().toString());
									this.getSender().sendMsgToSME("service,"+bsme.getBehaviourName()+","+(time+1.0), c.getSME().getName());
									c.setDone(true);
									bsme.setStartTime(time+1.0);
									allocateWorkTeam(bsme, time, c.getSME().getName());
								}
							}
						}				
				}
			}
			if(idleTeams>0 && !serviceRequestList.isEmpty()){
				for (ServiceRecord c: serviceRequestList){
					if(!c.isDone())
						if(!isInPremierList(c)){
							for(BehaviourSME bsme: c.getServiceList()){
								if(idleTeams>0){
									System.out.println(c.getSME().toString());
									this.getSender().sendMsgToSME("service,"+bsme.getBehaviourName()+","+(time+1.0), c.getSME().getName());
									c.setDone(true);
									bsme.setStartTime(time+1.0);
									allocateWorkTeam(bsme, time, c.getSME().getName());
								}
							}
						}				
				}
			}
		}

		this.receivedNewRequest= false;
		if(BuildModel.context.size()<=limit){
			printTeamsStatus();
//			saveData();
		}
			
		
	}

	private boolean isInPremierList(ServiceRecord c){
		boolean in=false;	
		for(SME sme: premierList){
			if(c.getSME().getName().equals(sme.getName())){
				in=true;
			}
		}
		return in;
	}
	private void createRecord(SME m, String msg){
		String[] temp= msg.split(",");
		ServiceRecord service= new ServiceRecord(m);
		service.setContactTime(time);
		System.out.println("              Service Record: "+ service.toString());
		serviceRequestList.add(service);
		if(msg.contains("clean")){
			BehaviourSME cleanUp=new BehaviourSME(m, temp[1] , 30, 100.0*BuildModel.TICK_PER_HOUR, false,25,"post",false);
			service.addService(cleanUp);
			System.out.println("%%%%%%%%%%%%%%%%%% CREATE A RECORD:"+service.toString());
		}
		if(msg.contains("electrician")){
			BehaviourSME electrician=new BehaviourSME(m, temp[1] , 30, 100.0*BuildModel.TICK_PER_HOUR, false,25,"post",false);
			service.addService(electrician);
		}

	}
	//This should be updated later
	private ManufactureSME searchSMEByName (String s){
		ManufactureSME found=new ManufactureSME();
		for(SME sme: BuildModel.smeContext){
			int i=sme.getName().compareTo(s);
			System.out.println(sme.getName()+"     vs     "+ s +"          "+i);			
			if(i==0){
				found =(ManufactureSME)sme;
			}
			else{
				System.out.println("------------------------Not this one-----------------");
			}
		}
		return found;
	}

	private void updateRequest() {
		
		LinkedList<Message> request = this.getReceiver().getReceivedMessages();
		if(request.size()>msgNumber){
			this.receivedNewRequest= true;
			//for (int i=request.size()-1; i>msgNumber-1; i--){
			String x= request.getLast().getMsgSender();
//			String msg= request.getLast();
			String msg=request.getLast().getMessage();				
			String[] temp= msg.split(",");
//		    String x=temp[3];
			ManufactureSME m = searchSMEByName(x); 
			System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Received from "+m.getName()+" "+msg+" at "+ time);
			if(m!=null){
				if(msg.contains("request")){						
					if(this.hasServiceRecord(m)){							
						BehaviourSME b=new BehaviourSME(m, temp[1], 30, 100.0, false,25,"post",false);
						this.getServiceRecord(m).addService(b);
						this.getServiceRecord(m).setDone(false);
					}else{
						addNewCustomer(m);
						createRecord(m, msg);						
					}						
				}
				
				if(msg.contains("finish")){
					for(ServiceRecord r: serviceRequestList){
						String r_name=r.getSME().getName();					
						if(m.getName().contains(r_name)){
							System.out.println(m.getName()+"                vs         "+ r_name);
							r.setDone(true);
							double t=Double.parseDouble(temp[2]);
							BehaviourSME b=r.getService(temp[1]);
							b.setEndTime(t);
							b.setDone(true);
							releaseTeams(b, m.getName());
							//searchServiceRecord(sme).getServiceList();
						}
					}
				}
			}
		}
		
		msgNumber=request.size();
		request.clear();	
		this.receivedNewRequest= false;
	}
	public boolean hasServiceRecord(SME sme){
		boolean b=false;
		for(ServiceRecord r: serviceRequestList){
			String r_name=r.getSME().getName();			
			if(sme.getName().equals(r_name)){
				b=true;
			}
		}
		return b;		
	}

	public ServiceRecord getServiceRecord(SME sme){
		ServiceRecord record=new ServiceRecord(sme);
		for(ServiceRecord r: serviceRequestList){
			String r_name=r.getSME().getName();			
			if(sme.getName().equals(r_name)){
				record=r;
			}
		}
		return record;		
	}
	public ServiceRecord searchServiceRecord(String sme){
		ManufactureSME m = searchSMEByName(sme); 
		ServiceRecord record=new ServiceRecord(m);
		for(ServiceRecord r: serviceRequestList){
			String r_name=r.getSME().getName();			
			if(m.getName().compareTo(r_name)==0){
				record=r;
			}
		}
		return record;		
	}
	public void allocateWorkTeam (BehaviourSME b, double time, String object){
		for(int i=0; i<numberTeams; i++){
			if(!b.isDone()){
				Employee t = teams[i];
				if(!t.isBusy()){
					t.setBusy(true);
					b.setStartTime(time);
					BehaviourHistory history = new BehaviourHistory(b);
					history.setObject(object);
					t.getBehaviourHistory().add(history);
					history.setStart(time);
					b.setDone(true);
					System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Allocate work to an available team:"+t.toString());
					idleTeams--;
				}
			}

		}
	}
	/**
	 * This method set the employees' status to not-busy and record the end time of the behaviour in the behaviour history.
	 * @param b
	 */
	public void releaseTeams(BehaviourSME b, String sme){
		for(int i=0; i<numberTeams; i++){
			Employee e = teams[i];			
			if(e.isBusy()){
				LinkedList<BehaviourHistory> behaviours = e.getBehaviourHistory();
				if(behaviours.getLast().getName().equals(b.getBehaviourName()) && behaviours.getLast().getObject().equals(sme)){					
					e.setBusy(false);
					this.idleTeams++;
					behaviours.getLast().setEnd(b.getEndTime());
					System.out.println("%%%%%%%%%%%%% print out the end time"+ behaviours.getLast().getEnd());
				}
			}
		}
	}
	

	public String getCompanyName(){
		return name;
	}
	public void setCompanyName(String s){
		name=s;
	}

	public LinkedList<SME> getCustomerList(){
		return serviceSequence;
	}
	public LinkedList<SME> getPremierCustomerList(){
		return premierList;
	}
	public LinkedList<ServiceRecord> getServiceRequestList(){
		return serviceRequestList;
	}

	public void addNewCustomer(SME sme){
		serviceSequence.add(sme);

	}
	public void addNewPremierCustomer(SME sme){
		premierList.add(sme);
	}
	public void addNewServiceRequest(ServiceRecord c){
		serviceRequestList.add(c);
	}

	public void saveData(){
		String[] entries = new String [4];
		entries[0] = "Teams";
		entries[1] = "Customers";
		entries[2]="Request Time";
		entries[3]="Details of Service";
		/*
		for (int i=0; i< this.serviceRequestList.size(); i++){
			entries [i+1] = serviceRequestList.get(i).getSME().getName();
			System.out.print(entries[i+1]+";");
		}*/
		writer.writeNext(entries);
		//String[] data = new String [this.serviceRequestList.size()+1];
		/*
		for(int i=0;i<data.length; i++){
			data[i]="";
		}

		writer.writeNext(data);*/

		for (int j=0; j<this.teams.length; j++){
			String[] team=new String[entries.length];
			for(int i=0;i<team.length; i++){
				team[i]="";
			}
			LinkedList<BehaviourHistory> h= teams[j].getBehaviourHistory();
			for(int x=1; x<=h.size(); x=x+3){
				team[x]=h.get(x-1).getObject();
				for(int temp=0; temp<serviceRequestList.size(); temp++){
					ServiceRecord sr= serviceRequestList.get(temp);
					if(sr.getSME().getName().equals(team[x])){
						team[x+1]=sr.getContactTime()+"";
					}
				}
				
				team[x+2]=h.get(x-1).toString();				
			}			
			team[0]="Team "+j;
			writer.writeNext(team);
		}
		try {
			writer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	public String toString(){
		String s=this.name+" received"+serviceRequestList.size() + "requests";
		return s;
	}
	public MsgSender getSender(){
		return sender;		
	}

	public MsgReceiver getReceiver(){
		return receiver;
	}
	public void printTeamsStatus() {
		for (int i=0; i< teams.length; i++){
			System.out.println(teams[i].toString());
		}

	}
	/**
	 * This method is called before the simulation is stopped to prepare to save the service company's data.
	 * It had bugs on BehaviourSME b= list.getLast(); after adding the if condition, it seems going well.
	 */
	public void prepareToStop(double time){
		for(int i=0; i<teams.length; i++){
			if(teams[i].isBusy()){
				BehaviourHistory bh= teams[i].getBehaviourHistory().getLast();
				ServiceRecord sr= searchServiceRecord(bh.getObject());
				LinkedList<BehaviourSME> list=sr.getServiceList();
				if(list.size()>0){
					BehaviourSME b= list.getLast();
					b.setEndTime(time);
				}
				releaseTeams(bh.getBehaviour(),teams[i].getBehaviourHistory().getLast().getObject());
			}
		}
	}
	public void printServiceRecord(){
		for (int i=0; i< serviceRequestList.size(); i++){
			System.out.println(serviceRequestList.get(i).toString());
		}
	}
}
